{
 "cells": [
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "### 一.FFM原理介绍\n",
    "FFM（Field-aware Factorization Machine）是对FM的改进，我们先回顾一下上一节的FM，它对于组合特征$x_ix_j$，设置的权重为$<v_i,v_j>$，而对于组合特征$x_ix_k$，设置的权重为$<x_i,x_k>$，可以发现这两项共享了同一个向量$v_i$，而FFM想法则是：   \n",
    "\n",
    "（1）若特征$x_j$与$x_k$属性类似，即属于同一个field，则利用同一向量$v_i$；   \n",
    "\n",
    "（2）若特征$x_j$与$x_k$属性不相似，即不属于同一个field，则使用不同的$v_i$；   \n",
    "\n",
    "这样学习出来的向量会更有区分性，所以对于$v_i$我们需要对其扩展一个维度来表示field，比如对于$x_ix_j$组合特征，有$v_i\\rightarrow v_{i,f_j}$，这里$f_j$即表示特征$j$所属的field，所以对于之前的FM模型方程，我们需要调整为：   \n",
    "\n",
    "$$\n",
    "y(x)=w_0+\\sum_{i=1}^nw_ix_i+\\sum_{i=1}^{n-1}\\sum_{j=i+1}^n<v_{i,f_j},v_{j,f_i}>x_ix_j\n",
    "$$  \n",
    "\n",
    "这里，$n$表示特征数，如果隐向量维度为$k$，field数量为$f$，那么FFM的组合特征项的参数将会有$nfk$个，是FM模型参数$nk$的$f$倍，那么如何判断不同的特征是否属于同一个field呢，这一般看业务需求，通常同一个特征做one-hot展开后的特征组属于同一个field，下面通过例子来直观感受一下FMM模型特征组合方式，假设某条输入记录如下：   \n",
    "![avatar](./source/17_FFM1.png)\n",
    "\n",
    "这条记录可以编码成5个特征，其中“Genre=Comedy”和“Genre=Drama”属于同一个field，“Price”是数值型，不用One-Hot编码转换。为了方便说明FFM的样本格式，我们将所有的特征和对应的field映射成整数编号。\n",
    "![avatar](./source/17_FFM2.png)\n",
    "那么，FFM的组合特征有$(4*5)/2=10$项，如下图所示  \n",
    "![avatar](./source/17_FFM3.png)  \n",
    "\n",
    "需要注意的一点是同一个field之间的特征也可以构造组合特征（虽然one-hot展开后的特征没有组合的必要），接下来我们还需要推导一下梯度的求解，通过求解如下：  \n",
    "\n",
    "\n",
    "$$\n",
    "\\frac{\\partial}{\\partial\\theta}y(x)=\\left\\{\\begin{matrix}\n",
    "1 &\\theta=w_0 \\\\ \n",
    "x_i &\\theta=w_i \\\\ \n",
    "v_{j,f_i,l}x_ix_j & \\theta=v_{i,f_j,l}\n",
    "\\end{matrix}\\right.\n",
    "$$  \n",
    "\n",
    "这里，需要特别说明一下，对于$v$，采用逐特征组合的方式进行更新，故$1\\leq i\\leq j\\leq n$，$1\\leq l\\leq k$表示隐含量维度，所以时间复杂度会比较高为$O(kn^2)$"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "### 二.代码实现\n",
    "这一节还是先对回归任务做实现，损失函数还是采用平方损失，同上一节一样，可以得到梯度：   \n",
    "\n",
    "$$\n",
    "\\frac{\\partial L(\\theta)}{\\partial\\theta}=(y(x)-t)\\cdot\\left\\{\\begin{matrix}\n",
    "1 &\\theta=w_0 \\\\ \n",
    "x_i &\\theta=w_i \\\\ \n",
    "v_{j,f_i,l}x_ix_j & \\theta=v_{i,f_j,l}\n",
    "\\end{matrix}\\right.\n",
    "$$   \n",
    "\n",
    "另外对FFM补充一个功能，对于输入的特征，我们也许并不是都想要参与特征组合，这时可以将这部分特征的field id设置为负数，将其过滤掉，其余的field id取值0,1,2...即可"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 1,
   "metadata": {},
   "outputs": [],
   "source": [
    "\"\"\"\n",
    "FFM因子分解机的实现，代码封装到ml_models.fm中\n",
    "\"\"\"\n",
    "import numpy as np\n",
    "\n",
    "\n",
    "class FFM(object):\n",
    "    def __init__(self, epochs=1, lr=1e-3, adjust_lr=True, batch_size=1, hidden_dim=4, lamb=1e-3, alpha=1e-3,\n",
    "                 normal=True, solver='adam', rho_1=0.9, rho_2=0.999, early_stopping_rounds=100):\n",
    "        \"\"\"\n",
    "\n",
    "        :param epochs: 迭代轮数\n",
    "        :param lr: 学习率\n",
    "        :param adjust_lr:是否根据特征数量再次调整学习率 max(lr,1/n_feature)\n",
    "        :param batch_size:\n",
    "        :param hidden_dim:隐变量维度\n",
    "        :param lamb:l2正则项系数\n",
    "        :param alpha:l1正则项系数\n",
    "        :param normal:是否归一化，默认用min-max归一化\n",
    "        :param solver:优化方式，包括sgd,adam,默认adam\n",
    "        :param rho_1:adam的rho_1的权重衰减,solver=adam时生效\n",
    "        :param rho_2:adam的rho_2的权重衰减,solver=adam时生效\n",
    "        :param early_stopping_rounds:对early_stopping进行支持，使用rmse作为评估指标，默认20\n",
    "        \"\"\"\n",
    "        self.epochs = epochs\n",
    "        self.lr = lr\n",
    "        self.adjust_lr = adjust_lr\n",
    "        self.batch_size = batch_size\n",
    "        self.hidden_dim = hidden_dim\n",
    "        self.lamb = lamb\n",
    "        self.alpha = alpha\n",
    "        self.solver = solver\n",
    "        self.rho_1 = rho_1\n",
    "        self.rho_2 = rho_2\n",
    "        self.early_stopping_rounds = early_stopping_rounds\n",
    "        # 初始化参数\n",
    "        self.w = None  # w_0,w_i\n",
    "        self.V = None  # v_{i,f}\n",
    "        # 归一化\n",
    "        self.normal = normal\n",
    "        if normal:\n",
    "            self.xmin = None\n",
    "            self.xmax = None\n",
    "        # 功能性参数\n",
    "        self.replace_ind = None  # 置换index\n",
    "        self.positive_ind = None  # 参与特征组合的开始id\n",
    "        self.fields = []  # replace_ind后的fields\n",
    "        self.field_num = None\n",
    "\n",
    "    def _y(self, X):\n",
    "        \"\"\"\n",
    "        实现y(x)的功能\n",
    "        :param X:\n",
    "        :return:\n",
    "        \"\"\"\n",
    "        # 去掉第一列bias以及非组合特征\n",
    "        X_ = X[:, self.positive_ind + 1:]\n",
    "        n_sample, n_feature = X_.shape\n",
    "        pol = np.zeros(n_sample)\n",
    "        for i in range(0, n_feature - 1):\n",
    "            for j in range(i + 1, n_feature):\n",
    "                pol += X_[:, i] * X_[:, j] * np.dot(self.V[i, self.fields[self.positive_ind + j]],\n",
    "                                                    self.V[j, self.fields[self.positive_ind + i]])\n",
    "        return X @ self.w.reshape(-1) + pol\n",
    "\n",
    "    def fit(self, X, y, eval_set=None, show_log=False, fields=None):\n",
    "        \"\"\"\n",
    "        :param X:\n",
    "        :param y:\n",
    "        :param eval_set:\n",
    "        :param show_log:\n",
    "        :param fields: 为None时，退化为FM\n",
    "        :return:\n",
    "        \"\"\"\n",
    "        X_o = X.copy()\n",
    "\n",
    "        # 归一化\n",
    "        if self.normal:\n",
    "            self.xmin = X.min(axis=0)\n",
    "            self.xmax = X.max(axis=0)\n",
    "            X = (X - self.xmin) / self.xmax\n",
    "\n",
    "        n_sample, n_feature = X.shape\n",
    "        # 处理fields\n",
    "        if fields is None:\n",
    "            self.replace_ind = list(range(0, n_feature))\n",
    "            self.positive_ind = 0\n",
    "            self.fields = [0] * n_feature\n",
    "            self.field_num = 1\n",
    "        else:\n",
    "            self.replace_ind = np.argsort(fields).tolist()\n",
    "            self.positive_ind = np.sum([1 if item < 0 else 0 for item in fields])\n",
    "            self.fields = sorted(fields)\n",
    "            self.field_num = len(set(self.fields[self.positive_ind:]))\n",
    "\n",
    "        # reshape X\n",
    "        X = X[:, self.replace_ind]\n",
    "\n",
    "        x_y = np.c_[np.ones(n_sample), X, y]\n",
    "        # 记录loss\n",
    "        train_losses = []\n",
    "        eval_losses = []\n",
    "        # 调整一下学习率\n",
    "        if self.adjust_lr:\n",
    "            self.lr = max(self.lr, 1 / n_feature)\n",
    "        # 初始化参数\n",
    "        self.w = np.random.random((n_feature + 1, 1)) * 1e-3\n",
    "        self.V = np.random.random((n_feature - self.positive_ind, self.field_num, self.hidden_dim)) * 1e-3\n",
    "        if self.solver == 'adam':\n",
    "            # 缓存梯度一阶，二阶估计\n",
    "            w_1 = np.zeros_like(self.w)\n",
    "            V_1 = np.zeros_like(self.V)\n",
    "            w_2 = np.zeros_like(self.w)\n",
    "            V_2 = np.zeros_like(self.V)\n",
    "        # 更新参数\n",
    "        count = 0\n",
    "        for epoch in range(self.epochs):\n",
    "            # 验证集记录\n",
    "            best_eval_value = np.power(2., 1023)\n",
    "            eval_count = 0\n",
    "            np.random.shuffle(x_y)\n",
    "            for index in range(x_y.shape[0] // self.batch_size):\n",
    "                count += 1\n",
    "                batch_x_y = x_y[self.batch_size * index:self.batch_size * (index + 1)]\n",
    "                batch_x = batch_x_y[:, :-1]\n",
    "                batch_y = batch_x_y[:, -1:]\n",
    "                # 计算y(x)-t\n",
    "                y_x_t = self._y(batch_x).reshape((-1, 1)) - batch_y\n",
    "                # 更新w\n",
    "                if self.solver == 'sgd':\n",
    "                    self.w = self.w - (self.lr * (np.sum(y_x_t * batch_x, axis=0) / self.batch_size).reshape(\n",
    "                        (-1, 1)) + self.lamb * self.w + self.alpha * np.where(self.w > 0, 1, 0))\n",
    "                elif self.solver == 'adam':\n",
    "                    w_reg = self.lamb * self.w + self.alpha * np.where(self.w > 0, 1, 0)\n",
    "                    w_grad = (np.sum(y_x_t * batch_x, axis=0) / self.batch_size).reshape(\n",
    "                        (-1, 1)) + w_reg\n",
    "                    w_1 = self.rho_1 * w_1 + (1 - self.rho_1) * w_grad\n",
    "                    w_2 = self.rho_2 * w_2 + (1 - self.rho_2) * w_grad * w_grad\n",
    "                    w_1_ = w_1 / (1 - np.power(self.rho_1, count))\n",
    "                    w_2_ = w_2 / (1 - np.power(self.rho_2, count))\n",
    "                    self.w = self.w - (self.lr * w_1_) / (np.sqrt(w_2_) + 1e-8)\n",
    "\n",
    "                # 更新 V\n",
    "                batch_x_ = batch_x[:, 1 + self.positive_ind:]\n",
    "                # 逐元素更新\n",
    "                for i in range(0, batch_x_.shape[1] - 1):\n",
    "                    for j in range(i + 1, batch_x_.shape[1]):\n",
    "                        for k in range(0, self.hidden_dim):\n",
    "                            v_reg_l = self.lamb * self.V[i, self.fields[self.positive_ind + j], k] + \\\n",
    "                                      self.alpha * (self.V[i, self.fields[self.positive_ind + j], k] > 0)\n",
    "\n",
    "                            v_grad_l = np.sum(y_x_t.reshape(-1) * batch_x_[:, i] * batch_x_[:, j] *\n",
    "                                              self.V[\n",
    "                                                  j, self.fields[self.positive_ind + i], k]) / self.batch_size + v_reg_l\n",
    "\n",
    "                            v_reg_r = self.lamb * self.V[j, self.fields[self.positive_ind + i], k] + \\\n",
    "                                      self.alpha * (self.V[j, self.fields[self.positive_ind + i], k] > 0)\n",
    "\n",
    "                            v_grad_r = np.sum(y_x_t.reshape(-1) * batch_x_[:, i] * batch_x_[:, j] *\n",
    "                                              self.V[\n",
    "                                                  i, self.fields[self.positive_ind + j], k]) / self.batch_size + v_reg_r\n",
    "\n",
    "                            if self.solver == \"sgd\":\n",
    "                                self.V[i, self.fields[self.positive_ind + j], k] -= self.lr * v_grad_l\n",
    "                                self.V[j, self.fields[self.positive_ind + i], k] -= self.lr * v_grad_r\n",
    "                            elif self.solver == \"adam\":\n",
    "                                V_1[i, self.fields[self.positive_ind + j], k] = self.rho_1 * V_1[\n",
    "                                    i, self.fields[self.positive_ind + j], k] + (1 - self.rho_1) * v_grad_l\n",
    "                                V_2[i, self.fields[self.positive_ind + j], k] = self.rho_2 * V_2[\n",
    "                                    i, self.fields[self.positive_ind + j], k] + (1 - self.rho_2) * v_grad_l * v_grad_l\n",
    "                                v_1_l = V_1[i, self.fields[self.positive_ind + j], k] / (\n",
    "                                1 - np.power(self.rho_1, count))\n",
    "                                v_2_l = V_2[i, self.fields[self.positive_ind + j], k] / (\n",
    "                                1 - np.power(self.rho_2, count))\n",
    "\n",
    "                                V_1[j, self.fields[self.positive_ind + i], k] = self.rho_1 * V_1[\n",
    "                                    j, self.fields[self.positive_ind + i], k] + (1 - self.rho_1) * v_grad_r\n",
    "                                V_2[j, self.fields[self.positive_ind + i], k] = self.rho_2 * V_2[\n",
    "                                    j, self.fields[self.positive_ind + i], k] + (1 - self.rho_2) * v_grad_r * v_grad_r\n",
    "                                v_1_r = V_1[j, self.fields[self.positive_ind + i], k] / (\n",
    "                                1 - np.power(self.rho_1, count))\n",
    "                                v_2_r = V_2[j, self.fields[self.positive_ind + i], k] / (\n",
    "                                1 - np.power(self.rho_2, count))\n",
    "\n",
    "                                self.V[i, self.fields[self.positive_ind + j], k] -= (self.lr * v_1_l) / (np.sqrt(v_2_l) + 1e-8)\n",
    "\n",
    "                                self.V[j, self.fields[self.positive_ind + i], k] -= (self.lr * v_1_r) / (np.sqrt(v_2_r) + 1e-8)\n",
    "\n",
    "                # 计算eval loss\n",
    "                eval_loss = None\n",
    "                if eval_set is not None:\n",
    "                    eval_x, eval_y = eval_set\n",
    "                    eval_loss = np.std(eval_y - self.predict(eval_x))\n",
    "                    eval_losses.append(eval_loss)\n",
    "                # 是否显示\n",
    "                if show_log:\n",
    "                    train_loss = np.std(y - self.predict(X_o))\n",
    "                    print(\"epoch:\", epoch + 1, \"/\", self.epochs, \",samples:\", (index + 1) * self.batch_size, \"/\",\n",
    "                          n_sample,\n",
    "                          \",train loss:\",\n",
    "                          train_loss, \",eval loss:\", eval_loss)\n",
    "                    train_losses.append(train_loss)\n",
    "                # 是否早停\n",
    "                if eval_loss is not None and self.early_stopping_rounds is not None:\n",
    "                    if eval_loss < best_eval_value:\n",
    "                        eval_count = 0\n",
    "                        best_eval_value = eval_loss\n",
    "                    else:\n",
    "                        eval_count += 1\n",
    "                    if eval_count >= self.early_stopping_rounds:\n",
    "                        print(\"---------------early_stopping-----------------------------\")\n",
    "                        break\n",
    "\n",
    "        return train_losses, eval_losses\n",
    "\n",
    "    def predict(self, X):\n",
    "        \"\"\"\n",
    "        :param X:\n",
    "        :return:\n",
    "        \"\"\"\n",
    "        # 归一化\n",
    "        if self.normal:\n",
    "            X = (X - self.xmin) / self.xmax\n",
    "        # reshape\n",
    "        X = X[:, self.replace_ind]\n",
    "        # 去掉非组合特征\n",
    "        X_ = X[:, self.positive_ind:]\n",
    "        n_sample, n_feature = X_.shape\n",
    "        pol = np.zeros(n_sample)\n",
    "        for i in range(0, n_feature - 1):\n",
    "            for j in range(i + 1, n_feature):\n",
    "                pol += X_[:, i] * X_[:, j] * np.dot(self.V[i, self.fields[self.positive_ind + j]],\n",
    "                                                    self.V[j, self.fields[self.positive_ind + i]])\n",
    "        return np.c_[np.ones(n_sample), X] @ self.w.reshape(-1) + pol"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "### 三.测试"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 2,
   "metadata": {},
   "outputs": [],
   "source": [
    "import matplotlib.pyplot as plt\n",
    "%matplotlib inline"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 3,
   "metadata": {},
   "outputs": [],
   "source": [
    "#造伪数据\n",
    "data1 = np.linspace(1, 10, num=100)\n",
    "data2 = np.linspace(1, 10, num=100) + np.random.random(size=100)\n",
    "data3 = np.linspace(10, 1, num=100)\n",
    "target = data1 * 2 + data3 * 0.1 + data2 * 1 + 10 * data1 * data2 + np.random.random(size=100)\n",
    "data = np.c_[data1, data2, data3]"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 4,
   "metadata": {},
   "outputs": [],
   "source": [
    "from sklearn.model_selection import train_test_split\n",
    "X_train, X_test, y_train, y_test = train_test_split(data, target, test_size=0.4, random_state=0)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "`fields`默认为None，这时FMM退化为FM，另外训练日志默认设置为了`show_log=False`"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 5,
   "metadata": {},
   "outputs": [],
   "source": [
    "#训练模型\n",
    "model = FFM(epochs=5)\n",
    "train_losses,eval_losses = model.fit(X_train, y_train, eval_set=(X_test,y_test))"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 6,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "[<matplotlib.lines.Line2D at 0x20738deb828>]"
      ]
     },
     "execution_count": 6,
     "metadata": {},
     "output_type": "execute_result"
    },
    {
     "data": {
      "image/png": "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\n",
      "text/plain": [
       "<matplotlib.figure.Figure at 0x20738deb860>"
      ]
     },
     "metadata": {},
     "output_type": "display_data"
    }
   ],
   "source": [
    "#查看拟合效果\n",
    "plt.scatter(data[:, 0], target)\n",
    "plt.plot(data[:, 0], model.predict(data), color='r')"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "从造伪数据的过程，我们知道只有第一个和第二个特征有交叉，我们可以将第三个特征的field id设置为-1，使其不参与交叉"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 7,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "[<matplotlib.lines.Line2D at 0x20736b56080>]"
      ]
     },
     "execution_count": 7,
     "metadata": {},
     "output_type": "execute_result"
    },
    {
     "data": {
      "image/png": "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\n",
      "text/plain": [
       "<matplotlib.figure.Figure at 0x20736b44be0>"
      ]
     },
     "metadata": {},
     "output_type": "display_data"
    }
   ],
   "source": [
    "model = FFM(epochs=5)\n",
    "train_losses,eval_losses = model.fit(X_train, y_train, eval_set=(X_test,y_test),fields=[0,0,-1])\n",
    "plt.scatter(data[:, 0], target)\n",
    "plt.plot(data[:, 0], model.predict(data), color='r')"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "上面第一个特征和第二个特征的归为了同一个field（都为0），当然我们也可以将它们归为不同的field"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 8,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "[<matplotlib.lines.Line2D at 0x2073af13b38>]"
      ]
     },
     "execution_count": 8,
     "metadata": {},
     "output_type": "execute_result"
    },
    {
     "data": {
      "image/png": "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\n",
      "text/plain": [
       "<matplotlib.figure.Figure at 0x2073af13a20>"
      ]
     },
     "metadata": {},
     "output_type": "display_data"
    }
   ],
   "source": [
    "model = FFM(epochs=5)\n",
    "train_losses,eval_losses = model.fit(X_train, y_train, eval_set=(X_test,y_test),fields=[0,1,-1])\n",
    "plt.scatter(data[:, 0], target)\n",
    "plt.plot(data[:, 0], model.predict(data), color='r')"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "当然，我们也可以将每个特征设置为不同的field..."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 9,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "[<matplotlib.lines.Line2D at 0x2073af814e0>]"
      ]
     },
     "execution_count": 9,
     "metadata": {},
     "output_type": "execute_result"
    },
    {
     "data": {
      "image/png": "iVBORw0KGgoAAAANSUhEUgAAAYAAAAD8CAYAAAB+UHOxAAAABHNCSVQICAgIfAhkiAAAAAlwSFlzAAALEgAACxIB0t1+/AAAADl0RVh0U29mdHdhcmUAbWF0cGxvdGxpYiB2ZXJzaW9uIDIuMS4yLCBodHRwOi8vbWF0cGxvdGxpYi5vcmcvNQv5yAAAIABJREFUeJzt3XlclNX+wPHPAQYYUBncZQDBDXdFcSVzTTItKds3K8t7b91u28/Sbrdst2yz5XbzlmVl21VES4vMPS0TRcUNd5FFQWVEZYQBzu+PWQTFJREGmO/79fI1M+dZ5jCver7P8z3f5zxKa40QQgjP4+XuDgghhHAPCQBCCOGhJAAIIYSHkgAghBAeSgKAEEJ4KAkAQgjhoSQACCGEh5IAIIQQHkoCgBBCeCgfd3fgfBo3bqwjIiLc3Q0hhKhV1q1bd1hr3eRC69XoABAREUFycrK7uyGEELWKUmr/xawnKSAhhPBQEgCEEMJDSQAQQggPJQFACCE8lAQAIYTwUBIAhBDCQ9XoMlAhhKiLElMymZqURpbFSojJyIS4KOKjzdXeD7kCEEKIapSYksmkhFRyD+dz88Ykso+eYFJCKokpmdXeFwkAQghRjaYmpWG1lRC3YzWv/fQeA/ZtwGorYWpSGmAPELFTlhA5cQGxU5ZUaWCQACCEENUoy2IFoNOh3QB0ObjT1e68Osi0WNFApsVapVcHEgCEEKIahZiMAHQ6tAeALgd3udqnJqVhLSqm1ZEM0Bqg3NXB5SYBQAghqtGEuCiMPl50yrEHgM4Hd2M0eDMhLoosi5Vwy0GWfPxXbt2Y5NrGedVwuUkAEEKIahQfbebtKxrT0JpPelAzzMdzeXNwCPHRZkJMRvrv3wjA2rBOrm2cVw2XmwQAIYSoZlfbDgIQPuFhAK4pygLsVwdXZGwmJzCY3Q1DAVxXB1VBAoAQQlS3lBRQCu65x/553ToA4ruHMOzgFja06YFSCrPJyKs3dKmyewTkRjAhhKhuKSnQti2YzfZXRwBg+3b8j+QyfMqt7L1/ZJV3Q64AhBCiuqWkQHS0/X3PnqcDwJIl9tchQ6qlGxIAhBCiOuXlwf795QNAejrk5sLSpdCyJURGVktXJAAIIUR12rDB/lo2AACsXQvLlsHgwfbxgWogAUAIIapTSor9tXt3+2uPHgAsnjgVjhzhhZPNq21eIBkEFkKIKlJ21s8gowGl4NnvfuCKBo1YlWkjvikk7jlBdHAIgzavAGBh4/Z8nZAKUOUzhMoVgBBCVIEz5/WxWG3kFdjoeGg3m5q0cs3xMzUpjU3NWuOtS9kTHMLBBo2rdPqHsi4YAJRSM5RSOUqpzWXaGiqlFimldjpegx3tSin1rlJql1Jqk1KqR5ltxjrW36mUGls1f44QQtQMzlk/u2bv4Mtv/smY1MXUKyyg9ZEMtjRt5TrIZ1mspDZvA8Dv4V1d21fV9A9lXcwVwGfA1We0TQQWa63bAosdnwFGAG0d/8YDH4I9YADPAX2A3sBzzqAhhBB1kfMAPnrrcq7Yv5E3F77Nyv+Mw0eXsqVZa9c6ISYjG0Lsd/r+GtHdtX1VTf9Q1gUDgNZ6BXD0jObRwEzH+5lAfJn2z7Xd74BJKdUCiAMWaa2Paq3zgEWcHVSEEKJWqmgOf+cBvMvBnawLac/Ym55nW9NICgx+pDgO+M6ngaVGduX2W17ix6j+QNVO/1DWpQ4CN9NaZwNorbOVUk0d7WbgQJn1Mhxt52oXQohazZnrt9pKgNNz+I/paSZxbTqdD+3mm65xLG/Vk+WtetqneVbKdZB3DvROTfIDixVzNT4i8nJXAVVUvKrP0372DpQajz19RHh4+OXrmRBCVAFnrr9hwTHMx3JIbdEWq62EpdtzebebHwG2QlJbtMXkqAKyFNjOeg5wfLTZLc8EvtQAcEgp1cJx9t8CyHG0ZwBhZdYLBbIc7YPOaF9W0Y611tOB6QAxMTEVBgkhhKgpnLn+R3/9iptTF9Hj4VkU+BrJslgZcuIQAG+/8QBEVX1K58+61DLQ+YCzkmcsMK9M+92OaqC+wDFHqigJGK6UCnYM/g53tAkhRK3mzPV3PrQL/+IiYh3z+YeYjJCcDA0a2Cd8q4Eupgz0a+A3IEoplaGUGgdMAa5SSu0ErnJ8BlgI7AF2Af8FHgTQWh8FXgTWOv694GgTQohabUJcFAE+iqjc/QAM3r329CDu2rUQEwNeNfOWqwumgLTWt51j0dAK1tXAQ+fYzwxgxp/qnRBC1HDx0WYCDuwj0HaKQm8DV+1NJuD6zsR3bAwbN8Ljj7u7i+dUM8OSEELUIsOL7bl+v788QJP8I8SrXNi0CWw2+xVADSUBQAghKmvTJnuaZ8IE++cFC+z5f4BevdzXrwuQyeCEEOIilZ3crVwp56ZN9oHeiAjyOnUja/pXbDGZuSrQxPIj3sS3dHfPKyYBQAghLsK5bvgCiN+4EXr2JDElkwONuvDQllkEW3JJadaGSXM3g1JuqfO/EEkBCSHEeTineXj02w2ug7+T1VbCB/NTYM8e6NaNqUlpJEXG4IUm5PhhNjluCquOmT0vhVwBCCHEOZQ962900sKo7Su5dtsK9gabmTDyUQDq79puX7lrV7JWWclq1oqcwGCansxjU3N7/X91zOx5KeQKQAghzmFqUhpFhUW8+uO7rPngbp7/5SMi8rK4afMvtDqSAUC/E46nd3XtSojJiFZeLG1lr/zZ1MIeAKpjZs9LIQFACCHO4Ez7HDx6gne+f4PbNv3MFz1GctV9H3D1fe9T5OXDHRt+xGjwZozhKAQFQXg4E+KiMBq8eS/2Vv7vmkc5HBhcbTN7XgpJAQkhRBnOtE9RYRHTvn+DUdtX8sqge5neZ4xrnaR2/bhp82KaTnuDVkt3Q9eu5QZ6pyb5MieoWbXO7HkpJAAIIUQZztk9X/n5wwoP/kaDN6Yn/kGDB24i9a2PGLR2PUk94/BJyXTN6llTD/hnkgAghBBlZFms+NkKGbN5MV93HV7u4O88oz+iNbsah3P3opnUL7KS3CCUedX0IPfLScYAhBCijBCTkZjMbfiV2Ehq19/VbjYZWTVxCPHRZqb+vIMvuo8gND8XgO1NI2t0uee5SAAQQogyJsRFMTh9A0VePqwJ6wyc/YjGLIuVhM5DKDD4UYoirXFLV3ttIikgIYQoIz7ajCVvO5tbduKUr3+FA7khJiOZFpjVfQTds3Zg9fV3tdcmEgCEEB6rwrl9Qn0xbd9Mj5deYu8/R1a43YS4KCYlpPLy4HGutppc7nkuEgCEEB7JWe5pOH4M5Wt0ze1jDtxDL4Bhw8657elyzwomhqtFJAAIITyS8y7fFf/9K4vb9GbiiH9gtZVwcM4PYDJdcB7/2lTueS4yCCyE8EhZFitRuftpUmDh1k0/03/fBtCa6B3JLDN3IXHTQXd3scpJABBCeKQQk5EeWfaJ3HICg3kl6QM65O4lND+XReYuTEpIJTEl0829rFoSAIQQHmlCXBS9stPIDTTxyLUTiLBk8+HcVwH4NaJ7razr/7MkAAghPFJ8tJmhlt1sbdmJ31p25Zuuw4mwZHMgqBn7TS2A2lfX/2dJABBCeKbcXOod2MfAsddhNhl5ZfB9ZNdrxKI2fUApoPbV9f9ZUgUkhPBMv/9uf+3blwn1WzMpoYihD/yHQh9foHbW9f9ZEgCEEHVehTd8/fYb+PhATAzxAQHA6br+mj6N8+WitNbu7sM5xcTE6OTkZHd3QwhRi535MHewn92v+OlFmuhCqIPHGKXUOq31+W9kQMYAhBB13NSkNEoLCnhs5Ze0zMsCoKiwiMBN66FfPzf3zr0kAAgh6iTnYx0z8wp4Y+E7PLL6G/4z9xX8iouIyt1PQNEpJuc2qPO1/ucjYwBCiDqnbNrnkVVfc+32lfwQdQWj0n7lyeUz2dvQnttfbGrFt7XwQS6XS6WuAJRSjymltiilNiulvlZK+SulIpVSa5RSO5VS3yqlfB3r+jk+73Isj7gcf4AQQpzJ+VjHUdtW8Niqr5jdeSh/H/0Un/a8lnHJ8xi77ntyA0wcCGrmETd8ncslBwCllBn4BxCjte4MeAO3Aq8Bb2ut2wJ5gHO+1HFAnta6DfC2Yz0hhPhTnKmdyIkLiJ2ypMIUTpbFSqsjGbyx8B3WmjvydNzfQSmmDLyH7Y1b0vbIAVLM7V31/nX9hq9zqewYgA9gVEr5AAFANjAEmO1YPhOId7wf7fiMY/lQpRy/vhBCXARnaqcwM4t7kueRlXeywjl7QoL8eXbxfyny8uHB+EkU+RgAKPb155HrJmD18WNVy26n16/jN3ydyyUHAK11JvAGkI79wH8MWAdYtNbFjtUyAGdizQwccGxb7Fi/0Zn7VUqNV0olK6WSc3NzL7V7Qog6yJnaefHnD3lu8X/pm55aLoXjvDqIWreCQXvXMe2K28mtFwzYSz9v6xNGekhr+jw0k897jHS11/Ubvs6lMimgYOxn9ZFACBAIjKhgVeeNBhWd7Z91E4LWerrWOkZrHdOkSZNL7Z4Qog7KslgZsHc9I3asBmBE2mpXu/PqIPdwPs8u/i87G4XxeY9RgP2B7q/e0IWX4rvw6g1dqN+8CSgvV7snDgBD5aqAhgF7tda5AEqpBKA/YFJK+TjO8kOBLMf6GUAYkOFIGQUBRyvx/UIIDxNez4fJv0xnb3AL9jQMJW7nbzx31V8ICQ50XR38LTmRCEs2d978IjZvH8wmI6smDnHtoy48yOVyqcwYQDrQVykV4MjlDwW2AkuBGx3rjAXmOd7Pd3zGsXyJrsm3IQshagxnaidu8be0PprB80PHM6/jQJqdOErfQzuZEBdFlsVK0+NH+Pvqb0lq25dfI6MBzx3gvRiVGQNYg30wdz2Q6tjXdOAp4HGl1C7sOf5PHJt8AjRytD8OTKxEv4UQHsKZ2ilJP8A/Vn/Doja9Wd66F0tb98Lm7cMLpTuIjzYTYjLy6KqvMZQUl3tYu6cO8F6MSt0IprV+DnjujOY9QO8K1j0F3FSZ7xNCeA7nBG6ZFiutjxzgk9kv4F1ayotDHkADDZo1xhA3nLYrk0Brnm/nzaBNP/NFj5GkB9vn8/fkAd6LIVNBCCFqHOdZf6bFSuy+Dcz94v8ILLJy+60vuw7uWRYrKTGDYd8+Rt73Hj7P/otifyOzr74HBR4/wHsxZCoIIUSN4xzQHbLrD6YnvMSuRmHcf+OzZAQ1c60TZDTwYH4YK5UXTy6fycC963l34F08MKaPHPQvklwBCCFqnCyLFbRmworP2dvQzI13Ti138DcavFEKsg31WBPemYF715MbaOI/Pa7z2GkdLoUEACFEjRNiMtJ//0Y65O5jeu8bOOEX4FrmTO1YCmwA/NSuPwDT+t9Gga9Rqn7+BEkBCSFqnAlxUZg+mURugIn5HQcC9rP+sjl95wDxd12Hc9LXyLyOgwCp+vkz5ApACFHjxBuPM2jXH8zrN5oiH98KB3QnxEVhNHhT6ONLQuehlHh5S9XPnyRXAEKIGsNZ+vm3b9/gJh8DIZMeY+/QbhWuW/ZKoNyzfmUA+KJJABBCVLuKHtIOMCkhFb/8PMZsXkJih0FMXpZNUcPG5zyoy7QOlSMBQAhRrZw1/vWP5tBCl5BJUyYlpOJv8MJqK+HuTT9jLC5kRq/Rrpk+5SBfNSQACCGq1dSkNKxFxcyZPRmv0lKuHvcBVlsJVlsJAHE7fmNDi3akNYkAZC6fqiSDwEKIapVlsXLFvg10zNlL+8P7CbMcdC0Lsh6nW/ZOlrXq6WqTqp6qIwFACFGtQkxGHlg7l3y/QACG7F4LgMloYEjGJrx1KSsiewAyl09VkwAghKhWL7TWDNy7nv/0GcPuhmaG7F6L0eDN5Os68WjxHvKN9dnUop3M5VMNZAxACHFZVVThEx9tdrU/OmsKVoMfC/pdS8OCY9ydspDXr27Ftd1DIHklXDuCXa9f5+4/wyNIABBCXDaJKZm8++liJv/4b+Z3uJLvOw5kUkIqyfuPMmddJvXychm9dRlfd48jx1CP9vffju9f53Ht4W2wuRCysuDqq939Z3gMCQBCiEpznt23SlnF7O/foKE1nwCble87DsRqK+HrNQco0ZqH1v+AT2kJM2LsJZ7/zA1ief36sGABtG1r39nw4e79YzyIBAAhRKU46/rv+vU7Ji77jB2Nw0kO7Ujsvg14l5ZQ4uVNiePpr/FblrG0dQz7g0MASD9RTGbvAfh+m8BOk5mmzSPZnAPxkvavFjIILISolKlJaficyGfiss9Y3KY319/1Jt+3H0Cg7RQdcvYC4K0UoZaDhObnsNxR4QP2Of3fD2hPk/wj9E/fxC/h0UxKSCUxJdNdf45HkQAghKiULIuVTod244Xmy+hrsPr6kxzaEYCemdswGry5rU8YAzM3A/BbeFfg9Jz+i1pGu/a1vFVP192/oupJABBCVEqIyUing7sB2NKsFQDZDZqQVb8xA3LSePWGLrwU34W/luznaD0TuxqHl5vT/3BgMBtatKXA4Mc6sz1wyN2/1UPGAIQQlTIhLgqvWXvJrteIw4HBgP3svrRff4ZtXQ/RZtCasI1rYGQce18b5drWOaf/y4PH0eL4EYp8DIDc/VtdJAAIISolPtpMfkEGm8LaocBV+x/adBj8PB/S08FqtZd4DhlSbtsJcVFMSkhlbVhnV5vc/Vt9JAAIISrn5Eka7NvFFf+6jb2TR55u17H219WrwWKxvx88uNymMqe/e0kAEEJUzqZNUFoKPXqUb+/aFQIDYdUqOHQIQkOhTZuzNpc5/d1HAoAQonLWr7e/nhkAfHzI7didvDk/0fBYLmujelO4IUsO9jWIBAAhROWsXw9NmoC5/IE9MSWTLL9wHsxeBcCS5h35ISEVQIJADSFloEKIylm/3n72r1S55qlJafzeor3r8+qW3aTGv4apVABQSpmUUrOVUtuVUtuUUv2UUg2VUouUUjsdr8GOdZVS6l2l1C6l1CalVI8L7V8IUcMVFsLmzWenf7DX8qeY21OKIj2oGZlBTV3tomao7BXANOAnrXV7oBuwDZgILNZatwUWOz4DjADaOv6NBz6s5HcLIdxt82YoLq4wAISYjBz3C2RJ6xgSOw4q1y5qhksOAEqpBsCVwCcAWusirbUFGA3MdKw2E4h3vB8NfK7tfgdMSqkWl9xzIYRbJaZkMuXlrwC4ec2ps+bvmRAXhdHgzf03PsdbV94FSI1/TVOZK4BWQC7wqVIqRSn1sVIqEGimtc4GcLw2daxvBg6U2T7D0SaEqGWcM4CG7t1Gvl8gf3iZzprELT7azKs3dMFsMqJAnvBVA1WmCsgH6AE8rLVeo5Saxul0T0VUBW36rJWUGo89RUR4eHgluieEuBTneqJXWVOT0rDaSuh8aLd9/h+lXAO8ZdeVGv+arTIBIAPI0FqvcXyejT0AHFJKtdBaZztSPDll1g8rs30okHXmTrXW04HpADExMWcFCCHE5ec86GdarChOn5llWqxM+N9Gnv9+C5YCmysgZFms+NtO0T53H19EX+Pajwzw1i6XHAC01geVUgeUUlFa6zRgKLDV8W8sMMXxOs+xyXzg70qpb4A+wDFnqkgI4T7OdE6Do4d4Z9mnBNgK8S4twebtw+zOw/ilTW/yCmyAPSBMSkjFFGDgrwtm4F9cRFK7fq59yQBv7VLZG8EeBmYppXyBPcC92McVvlNKjQPSgZsc6y4ErgF2AQWOdYUQbuZM59yXupj4rcvZ2jSSYi9vGp+0cPWO39jaNJJ3+9/KT+36u1I97fZv5f61iXzV7WqSQzsBMsBbG1UqAGitNwAxFSwaWsG6GnioMt8nhLj8nGmbQXvWsal5G64b+w4A3qUlXLd1OX//7Vv+k/gqyyJ7MuGaRzjmX5/Xf5xGTmAwUwbbz+PMMolbrSRTQQjh4UJMRk5k59Ajazsf9L3J1V7i5c3czkOY13Egd2z4kaeXfkrSjL+zJqwzUYfTuffG58j3C8RsMrJq4pDzfIOoqWQqCCE83IS4KIYe2Ii3LmVZa/sFvbNkz2Q04G3w4Yseoxh1zzscCGrGiB2rmdtxEEtb9wJk4Lc2kysAITxcfLSZHsW7OWasz8YW7c5K5zgrhHYTxpg7pzJ85+/lHuwuA7+1l7Kn5mummJgYnZyc7O5uCFGrXbCuv7QUQkLsD2v5+uvz7mdSQipWW4mrzWjwlpu7aiCl1DqtdUXjs+XIFYAQddiZB21nGSeUmZJ5wwb7A1tGjDjvvuTpXXWPBAAh6jBniedNm37mhi1LuW/Mc1jxd03JPDUpjesXfsb/AT+GdOX8IUDu7K1rZBBYiDosy2KlwakTPLPkE/qlp/LEyi+A01cCmRYrAx3ln48vP3jWhG6ibpMAIEQdFmIycv8fcwkqPMmKiGjuXfc93bPsZ/9WWwlB1uP0yNrOssie8rAWDyQBQIg67J+9GjEueR4/tB/Ag/GTOFSvIa/9OA1DiY0g63GeXjrDXv7Zyj5eKCWdnkXGAISog5yVP/fOeRf/4iI+vmosJ/wCeGb4g8yY8wL/mfsKPTO3Ub+wgJk9RpJitk/hICWdnkUCgBB1jLPyJ+jIIe5KWciczkNICzKDrYQlbXozr8NARm9bzsqW3Xl5yDi2N40EZC4fTyQBQIg6xln588ryz1Ba827sbVhtJXgrRYnWPHnNI/y39/Vsbtba9SB3mcvHM0kAEKKWudCNXVkWK6O2reD6rct4J/Y2MoKaAVCiNUaDN1Z82dy8DSA3cnk6CQBC1CLO9M4jiz7mpMGf92Jvc93YBfaz/+b5ubyc9AEpLaJ4r/+trmXOs3y5kUs4SQAQohaZmpSG7VQhd6//gQBbIevMHVgd0Z3J87dQWFzKqSIbsxa8jU9pCY9e+wQlXt7A6fy+3MglypIyUCFqkSyLlY45ewiwFVLo7cPrP06jXmEBFqsNa1ExT6z8kv7pm5g8bDz7g0MAeRi7ODe5AhCihqoo1x9iMtJr7VYA/nHtk/x73hT+ueRjXh5yP1MXvsOIHav5X+dh/K/LVYB9WmeZq1+ciwQAIWqgxJRMnpm9gfuXz2J5ZE9SaM+khFTG9DTTe+Y20oOakRTVn+m9b+Bva2Zz5d4Ump04wouDx/FJr3hXdY/U9YvzkRSQEDXQmz9u5aXEqTy66mueXDETsE/dsHRbDgNzd7ClVVcA3r7iDrY1icC3xMYdt77MJ72vdx38pa5fXIhcAQhRgySmZDL1p+089N2bxG9dzvbGLemTvpmmx4+QU78Rhn178D+Sy4hXbsR81EimBcbcORWAAt/TZ/tS1y8uhlwBCFFDOEs875n7PrdvTOK9frfw0OiJeKEZmfYrAFcd3WlfOTaWCXFRGA3eFPgaXQd/o8Gbd27pzqqJQ+TgLy5IAoAQNcTUpDRCsvfxwNpEvuw+gjcH3MnuxmFsbRrJtdtWYDR4c0fxAQgOhg4diI828+oNXTCbjCik2kf8eZICEsINKqrwybJY+efGnyjy8uGtAXe6cvnfd7iSp5bPZFpfExH/S4HYWPCyn7tJXb+oDLkCEKKaOVM9lkNH0Jx+OEsTX80NW5byc9u+HA0Icq2/tre9pPPwa29DWhr/Lm4uD24Rl4UEACGq2dSkNBoezibl3dsZu+57wF7hM3jrahpa8/mmW5xrXaPBm/b9u7IxJIobVyUAsLhROyYlpEoQEJUmAUCIapZlsdJ//wZ8S4t5eukM2hxOB+C65AWcNIezr1vfcjn9pdtzmd9+AL6lxRR6G0ht3lae3iUuCwkAQlSzEJOR3ge2YvGvxwlfI2//8CatDx8gdv8mvuw0jP8b0YG9U0a6KnmyLFZ+aH8FpSg2tmhLkY8BkKd3icqTQWAhqtmEuCiiX9/CmrDOJHQewkdzX+GL7/5FsfLikzYDOe6Y3dM5uBtiMpJJY97rfytbHQ9vcbYLURmVvgJQSnkrpVKUUj84PkcqpdYopXYqpb5VSvk62v0cn3c5lkdU9ruFqI3im3vRMi+btLbdSWrXn9mdhxJy/DBLW/cip36js9I7znr/twfcQVJUf0Du8hWXx+VIAT0CbCvz+TXgba11WyAPGOdoHwfkaa3bAG871hPC86xcCcA/nrsXBTw/bDwJnQbzzhW3u1Ypm96Ren9RVSqVAlJKhQIjgZeBx5VSChgCOP9LnglMBj4ERjveA8wG3ldKKa21rkwfhKh1Vq6EgACIjiZkyUoyLfD4qCfKrXJmekfq/UVVqOwVwDvAk0Cp43MjwKK1LnZ8zgCc/9WagQMAjuXHHOsLUWckpmQSO2UJkRMXEDtlScWlmr/+Cv36gcHgSu+UJekdUV0uOQAopUYBOVrrdWWbK1hVX8Sysvsdr5RKVkol5+bmXmr3hKh2zhu8TmTncMuGnzh05PjZ9frHjsHGjTBgACDpHeFelUkBxQLXKaWuAfyBBtivCExKKR/HWX4okOVYPwMIAzKUUj5AEHD0zJ1qracD0wFiYmIkPSRqjalJaVhtJby0eDpjtiylQeFJpvcZw9SktNMH9NWrQWtXAABJ7wj3ueQrAK31JK11qNY6ArgVWKK1vgNYCtzoWG0sMM/xfr7jM47lSyT/L+qSLIuVrtk7GLNlKfm+ATy66ivMx3JcA7qJKZl8/voX2Ly8GfLrKbmTV7hdVdwI9hT2AeFd2HP8nzjaPwEaOdofByZWwXcL4TYhQf78a/HH5AaYuPHO1wGY/MtHaKD78z8zYfZG2u/ayOZmbdhToGU6B+F2lyUAaK2Xaa1HOd7v0Vr31lq30VrfpLUudLSfcnxu41i+53J8txDu5hz47bbmF3plbuWtAXeyo0kEb8fewVW71jB8x29YrDb8C07QLXsHf4R1ApDpHITbyZ3AQlSCc+C31Gpl4rJP2dYkgu+62mfv/DTmOm7YsoRXkt7nsV9n0e5wOt66lN/Du7i2l+kchDvJXEBCVIJz4PeedfMJP3aIl4bcT4mXvayz2NuHSVc/zElfIzn1GvJ+v1u46+YXWNaqp2t7mc5BuJNcAQhxHhU9uKVsxU6WxUrDgmM8tPo7FrfuxaqI7uW23xASxcC/fFzhvqXeX7ibBAAhzsGZ3gnN2sOVxw+zumVXHvt2A49+u8H10PUoDyB6AAAU90lEQVQQk5Hxiz4kwHaKVwbd59rWZDRQWFyK1VbiajN4Ker5+2ApsFUYTISobhIAhDiD86xfp+/npZVfcv3mpXihORwQRGLHQXzTLY5dhPPYtxtodeQAd6T8yFfdR7C7cRhgP7OffJ19oPd8Vw9CuJsEAOGxKkrvAExKSOXa5B95cdGHAEzvfT3rQjty/eYl3L1+Afcnz2Neh4G8fcXtTFo6gwKDP9McE7mZzzjQywFf1GQSAIRHcqZ32u/bgp9/PfYQyqSEVPwNXhQVFjFhxedsadaKv49+iqwGTQFY1LYvDQuOcV/yPO5LnsfI7Svx0aVMGXgPRwKCMJuMrJo4xM1/mRAXTwKA8EhTk9IoLSjgs/89R1aDJoy49z2sthKsthIG7U2hSYGFZ+IedB38nY4GBPHGlXfzWc9reei374jIy+LTmOsAKekUtY8EAOGRsixWrtuxmqDCkwTlnqR3xhb+COsMwPVblmLxr8fSVr3KbaM4PXvh4cBgnh/2l3LLpaRT1DZyH4DwSCEmI7du+pn0oGbk+dfnnuT59nYvG8N3/s4P7Qe4nr1rNHjzzi3defuW7pgdB/kzp7aVkk5RG8kVgPBIkzsY6JeeyutX3k39wgLG/5FAq4IjvNHoCMbiQlb2HYGCs6p3nK8Xuj9AiNpAAoDwSFf9toBSb29WxY7kSN5Jxv+RwJ0pC7Ee2EZGwxBGPHA9H/UIPef2MoWzqAskAAiPkpiSyVsLtzDn/Y/Y3q4P994UC8DiZf24ce0P1Css4N3YW/lo7mZQSg7yok6TACDqjAulZZylnwO2/EqTkxae6jiM3xyln+2iRzF8+yoA5nYa7JqpUwKAqMskAIg6wXlwb5WxA/zrk6mbnDVtg3Pitts2/sTBeg1Z3qonJY7SzzVhnUlt1poCXyP7g0MAKesUdZ8EAFEnTE1KIzRrDws+ewSAg/Uass7cgXdjbyONCCYlpGK1lRC3YzWD96xj6oC7XLN2AqAUd9z6Mlqdru+Rsk5R10kZqKgTsixWxmxejM3LmxeH3M/v4V3ol57Kl98+Q3heNlZbCU1O5PHKT++T2qw10/vc4NrWZDRgNHiT71+P436BgJR1Cs8gVwCiTght4Mv1W5exrFUMn/SKB6D1kQPM/vJJPv/uWW6643Ve//EdAm2neHTU/2HzPl3jLxO3CU8lAUDUCa8F5dDsxFHmdD49F8/uRmHce9NkvvrmaRZ++jBNCiw8O+wvrlk7ZeI24ekkBSTqhP6rF1JUP4jtPa8ETt+puyEkigdHTyLYms/yyB583mOUa/mqiUPkoC88mlwBiNovPx/mzsV37FiWPXM1cLokNNNiZVnrGIbd/yHZ9RuDY5BXBniFkCsAUQskpmQSO2UJkRMXEDtlCYkpmeWWvXzfS2C1Ml51ci2LjzazauIQ3rmlO0aDN/samik0+AEywCuEk1wBCLe62Ju3nI9WzLRYmZSQ6lo+KSGVGWt/Yk9wCD/Xj2ClY9mZeX0Z4BXibBIAhNtc6ODuTOH4Ftt4ctUsxmxewkOjnyI5tBNTk9IAaJu+jX7pqbwx4E5QqsI7eGXeHiEqJikg4TbOO3PHr5nDrx/ei7/tFFZbCZPnb2FSQiqZFitds3fww2eP8ODvs/ErLmJ6wsuE52WTabGi0/fz8ZwXyWjQlFndR7j2K3fwCnFxJAAIt8myWAkosvLg7/8jND+Xa7etBMBitWG1lTBwzzoSvvg/6hee5J4bJzP67rdQWjNj9vOE5Ocw43/P419cxD03TSYvIMi1XxngFeLiSAAQbhNiMnLbxiRMp05wOCCIO1MWnl6oNRNWfM4BUzPixn3AstYx7A8O4S83/JNwy0EW//dvtD6awd/iJ7GrcbhrMxngFeLiXXIAUEqFKaWWKqW2KaW2KKUecbQ3VEotUkrtdLwGO9qVUupdpdQupdQmpVSPy/VHiNrpySGRPLB2Lr+Fd2Fa7G10O7iTXrm7CA4wMHhPMp0P7ebffW8m37+ea5s/wjoz6eqH8S4t4Z/DH2JVRHfXMrPJyKs3dJF8vxAXqTJXAMXAE1rrDkBf4CGlVEdgIrBYa90WWOz4DDACaOv4Nx74sBLfLWoxZ1nnr5On0fz4Eb688hYSOw2hwODPnesXkHeyiH+s+oYDQc2Y22kwcPqxjGaTkTldhtLl0W/5rttw1z7NJqPc2CXEn3TJAUBrna21Xu94fxzYBpiB0cBMx2ozgXjH+9HA59rud8CklGpxyT0XtZKz8icr7yR/XTOHrU0jWdKyB6MHtmd+lyHEbV7OqO0ric5O48O+N1Ls7VPuzH5CXBRGg7erph8k7SPEpbosYwBKqQggGlgDNNNaZ4M9SABNHauZgQNlNstwtAkP4qz8idvxG62PZvBhnxuxFpfy9ZoDfNZtBP7FRby54G2y6zVidudhZ53Zx0ebefWGLphNRhSS9hGiMip9H4BSqh4wB3hUa52vysynfuaqFbTpCvY3HnuKiPDw8LM2ELVT2akZwvOyeTnpA3Y1DGVh+ysAKNGa7U0j+SO0I70ztvJRnzEU+RgqLOmUun4hLo9KXQEopQzYD/6ztNYJjuZDztSO4zXH0Z4BhJXZPBTIOnOfWuvpWusYrXVMkyZNKtM9UYXONz1DRes66/qDrMf5dPbzeGnNA2P+5Xooi7fjxOGd2NtZFtmTr7vFAVLSKURVqkwVkAI+AbZprd8qs2g+MNbxfiwwr0z73Y5qoL7AMWeqSNQuZQ/omtN38J4rCDjTPr7FNqbPfZnQYwd5YMwz7G1oP4s3Gry5rU8YRoM3qyO6c8/Nz1No8JPcvhBVrDIpoFjgLiBVKbXB0fY0MAX4Tik1DkgHbnIsWwhcA+wCCoB7K/Hdwo2mJqVRWFjEY6u+YVOLNixu08c1BYNzeZbFSpDRgFKQV2DDt9jGtO+n0ufAZh6+dgLJofaHsJSdkz+mZUOZs0eIaqS0PisNX2PExMTo5ORkd3dDnCHyqR94dvF07l33PcXKi8dHPcH8jgMB+0DPmf9FBRRZ+c/cV7hyXwqTh47ns5jrgNOlm0KIy0sptU5rHXOh9WQyOHFeZWfrdJ7R37c2kXvXfc/n0SNpeySdt394Ey9dSmKnweUO/l6lJTQ7cZR/J06hy8GdPHHNY8zpMhSQ0k0hagIJAOKczpyt81hBIfFblvGvpZ+wICqW5676C37FRXwy5wXe+uEtbti8hHpFBQSdOkGw9Tgm63G80BR6G/jb9U+zqG1f4OxHMQoh3EMCgDinqUlpWIuKuW1jEsN2rSEmYytBhSf5I7Qjj496Aq28OGXwZ9yYZ3k56QPaHU4nz9iAzAZNsRjrc9QYxNGABvwR1oltTVsBkvYRoiaRAOChznwQy+D2TVi6PbfcAGyWxcqo7St5Nel99gSHsDAqlrVhnfixXSyFPr6ufZ0y+PPEqCcu+J2S9hGiZpFBYA90ZmoHoNFJC81PHGFr00i08sJo8KZxiZU5747jYP1GxN/1JqWOmv2yTEYDhcWl5fblHAg2OcYMLAU2qeoRohrJILA4J2ddfuOTebyc9AHRWWk0PZkHwIKoWB4f9QRWfBm3aAaNCo5x343PVXjwNxq8mXxdJ9c+pXxTiNpFAkAdV9Ezd53TKzyx4gsG705mfseBbGsaSYNTJ3lk9dc0tObzXr9buHv9Amb2HMWW5m2A85/RywFfiNpHAkAd5kz12E4VEpafSzotmJSQiinAgCljHzel/sIXPUby/LC/uLbZ0zCEqQun8dW3z3CoXkPeGnAnGhm8FaIukgBQB5WdeK3BqRN8nPgKsfs38fjIx0joPBSrrYQXV35JoY8v7/e7pdy28zoN5nBgMK/9+C7PDxvPcb9AQJ6zK0RdJAGgjik7wGs+lsOM2ZOJPJrFtiYRvL5wGnnGBuQEBjNq+0re63cLRwJNmMtUAWVarKyK6M4Vf5tRbr8yKZsQdY8EgFqmopx+2fy7c4C3fc5ePv/uWfyLixh78wtsat6Gr795mn8nTmFPQzN5/vWZ3ueGs1I7FVUISfmmEHWTBIBa5MyDs3MWTidn2qf1kQN8+e0zFHkbGHPH6+xs0hKAe2+czP9mPUmnnD28MuhejvsFcuKM1I4zmEhVjxB1n9wHUIvETllCpsVKj4xtNLTm80vbPkD5WvzQY4f435dP4lNaws13vOaactnJfCyH+C1L+bhXPIUGPxncFaIOutj7AC7LIyFF1XI+fCXTYqVd7j6++O5fTE94ieE7fgPAYrVhtZXQ9PgRvvzmGQJsp7jrlhfLzbd/Z99wjAZvMoOa8kH/W2S+fSGEpIBqkory+4Ar7dPg1AmmJ7zMCb8AdjUK450f3uDm219jc/M29Mjcxr8TX6V+YQF33vIS25tGAjLfvhDi3CQFVENUNPhadm59r9ISPp39PP32b+LW218l3dScxM+fwFBazMweo3j016/IbtCYv17/tEy8JoSHk6kgapiLrd4pSwNoTdeDOxn/x1wG7l3PU1c/zHpzBwDuu/FZ5nw5gSdXfM7i1r14bNQT5PvXA6RyRwhxYRIAqsHFVu8EFFn51+L/0tJyEIt/PfL969EjczvtjqRzyseXd/vdwreOh6V7K8WOJhGMvfkF2ufu4+tucWhlH9KR+faFEBdDUkDVwDmA62crtE+jrJRrmTPNE5Kfw8dzXqRd7n42tmhH/cICTKeOs9/Ugjmdh7CgwwDXXblGgzdjepqZsy7zrHr9V2/oIgd+ITycpIBqgLJTMlyxN4WPE15knbk9LwwdT1qTCMB+8O+RsY2PEl/Gz1bEfTc+x4pWPc+5TxnUFUJcLnIFcAnOl88ve9B3nt33OrCZz797juz6jQi2HqdB4Um+6TYcm5cPg/asI8KSTXpQM+678Tl2NQ4Hzn64upzdCyEu1sVeAUgA+JPOV61jMho4WVSMreT0b9oleydfffM0OfUaccvtr2Lz8uGxX2dxZ8pCinwMrA7vyvJWPZnfYSDHjPWB02f5cnYvhLgUEgD+hLJn9EHnmPO+7Jk9QIv8XMZsXsys7iPICwhy7UvpUq7Yt4HuWWl0ObSbfvs3YjE24KbbX+Ngg8au9YILjlHgayz3aEWQM30hROVJAKhARQf6vALbWemWspzLyq4TXHCM2bOepPXRTHICg3lyxCMsax1Dl+ydvLjoQ7pn76AUxd6GZja0aMvbV9xBhqk5cP5HKEr1jhDicpBB4DOcmbqxWG2uZecLgfqM14AiK5/Ofh5zfi5PXf0w9ybP57PZk1kT2oleGVs5HGjiiWse46d2/TjpF1BuX/IIRSFETVInA8C5zvQr4mcr5NrtK7kzZSHhloPsbBTGjiYt2dE4nL3BZvY2NJMbGEz9wpM0KDzJ5F8+osvBXfzl+n/yS9s+JHYazBMrvuDulAV80ms002Jv50SZA/+5zu7lgC+EcLc6lwKqaJDWyaekmFs2/UyXg7sw2goJsJ2iV8YWTKdOsLNRGOvMHWh9JIOo3H00KCo453c8efU/+K7bcKBMikiXopUXBi9FPX+fCp+bK4QQ1cFjU0DOKRXityzF4l+frU0jyanXkEF71vHMko9pczSDnMBgjvsFcMrHj5UR0cyKHsHvYV1O36ClNc1OHCEyL4uIo1k0KjjGcb8AjvnXY19wCBtDolyDtc7vzLJYJYcvhKhVqj0AKKWuBqYB3sDHWuspl3P/WRYraM0Li/5Dg8KTAOT7BtCgqIA9wSHcN+ZZlrTuVe5uXChfyqkUHFKNyanfmN/Du561jqRzhBB1QbUGAKWUN/ABcBWQAaxVSs3XWm+9XN8RYjLa77z96ye0z91H+5y9tM/dz47G4cyKHoHN2wCcPtCfL1VzoQnchBCiNqvWMQClVD9gstY6zvF5EoDW+tWK1r/cYwAgdfZCiLqvpo4BmIEDZT5nAH0u5xec+Uzbc93YJYQQnq66A4CqoK3cJYhSajwwHiA8PPySviQ+2iwHeSGEuIDqfiZwBhBW5nMokFV2Ba31dK11jNY6pkmTJtXaOSGE8CTVHQDWAm2VUpFKKV/gVmB+NfdBCCEE1ZwC0loXK6X+DiRhLwOdobXeUp19EEIIYVft9wForRcCC6v7e4UQQpRX3SkgIYQQNYQEACGE8FA1ejI4pVQusN/d/bgMGgOH3d2JGkJ+i/Lk9zhNfovyKvN7tNRaX7CMskYHgLpCKZV8MXfleQL5LcqT3+M0+S3Kq47fQ1JAQgjhoSQACCGEh5IAUD2mu7sDNYj8FuXJ73Ga/BblVfnvIWMAQgjhoeQKQAghPJQEgCqilApTSi1VSm1TSm1RSj3i7j65m1LKWymVopT6wd19cTellEkpNVsptd3x30g/d/fJnZRSjzn+P9mslPpaKeXv7j5VF6XUDKVUjlJqc5m2hkqpRUqpnY7X4Kr4bgkAVacYeEJr3QHoCzyklOro5j652yPANnd3ooaYBvyktW4PdMODfxellBn4BxCjte6MfZ6wW93bq2r1GXD1GW0TgcVa67bAYsfny04CQBXRWmdrrdc73h/H/j+4xz6kQCkVCowEPnZ3X9xNKdUAuBL4BEBrXaS1tri3V27nAxiVUj5AAGdME1+Xaa1XAEfPaB4NzHS8nwnEV8V3SwCoBkqpCCAaWOPenrjVO8CTQKm7O1IDtAJygU8dKbGPlVKB7u6Uu2itM4E3gHQgGzimtf7Zvb1yu2Za62ywn0wCTaviSyQAVDGlVD1gDvCo1jrf3f1xB6XUKCBHa73O3X2pIXyAHsCHWuto4CRVdIlfGzjy26OBSCAECFRK3eneXnkGCQBVSCllwH7wn6W1TnB3f9woFrhOKbUP+AYYopT60r1dcqsMIENr7bwinI09IHiqYcBerXWu1toGJAD93dwndzuklGoB4HjNqYovkQBQRZRSCnuOd5vW+i1398edtNaTtNahWusI7IN7S7TWHnuGp7U+CBxQSkU5moYCW93YJXdLB/oqpQIc/98MxYMHxR3mA2Md78cC86riS6r9gTAeJBa4C0hVSm1wtD3teCCOEA8DsxyPRt0D3Ovm/riN1nqNUmo2sB579VwKHnRXsFLqa2AQ0FgplQE8B0wBvlNKjcMeIG+qku+WO4GFEMIzSQpICCE8lAQAIYTwUBIAhBDCQ0kAEEIIDyUBQAghPJQEACGE8FASAIQQwkNJABBCCA/1/8cPSZzMpsEeAAAAAElFTkSuQmCC\n",
      "text/plain": [
       "<matplotlib.figure.Figure at 0x2073af81128>"
      ]
     },
     "metadata": {},
     "output_type": "display_data"
    }
   ],
   "source": [
    "model = FFM(epochs=5)\n",
    "train_losses,eval_losses = model.fit(X_train, y_train, eval_set=(X_test,y_test),fields=[0,1,2])\n",
    "plt.scatter(data[:, 0], target)\n",
    "plt.plot(data[:, 0], model.predict(data), color='r')"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": []
  }
 ],
 "metadata": {
  "kernelspec": {
   "display_name": "Python 3",
   "language": "python",
   "name": "python3"
  },
  "language_info": {
   "codemirror_mode": {
    "name": "ipython",
    "version": 3
   },
   "file_extension": ".py",
   "mimetype": "text/x-python",
   "name": "python",
   "nbconvert_exporter": "python",
   "pygments_lexer": "ipython3",
   "version": "3.6.4"
  }
 },
 "nbformat": 4,
 "nbformat_minor": 2
}
